| 1 | <?xml version="1.0" encoding="utf-8" ?> |
| 2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
| 3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| 4 | <html xmlns="http://www.w3.org/1999/xhtml"> |
| 5 | <head> |
| 6 | <title>Metaobject Protocols</title> |
| 7 | <meta name="generator" content="muse.el" /> |
| 8 | <meta http-equiv="Content-Type" |
| 9 | content="text/html; charset=utf-8" /> |
| 10 | <link href="http://feeds.unknownlamer.org/rss/site-updates" |
| 11 | rel="alternate" type="application/rss+xml" title="Updates Feed" /> |
| 12 | |
| 13 | <link rel="stylesheet" href="default.css" media="screen" /> |
| 14 | </head> |
| 15 | <body> |
| 16 | <h1>Metaobject Protocols</h1> |
| 17 | <div class="contents"> |
| 18 | <dl> |
| 19 | <dt> |
| 20 | <a href="#sec1">Background</a> |
| 21 | </dt> |
| 22 | <dd> |
| 23 | <dl> |
| 24 | <dt> |
| 25 | <a href="#sec2">Object Protocols</a> |
| 26 | </dt> |
| 27 | <dt> |
| 28 | <a href="#sec3">CLOS Way of OO</a> |
| 29 | </dt> |
| 30 | <dd> |
| 31 | <dl> |
| 32 | <dt> |
| 33 | <a href="#sec4">Classes for Scratch Data and Types</a> |
| 34 | </dt> |
| 35 | <dt> |
| 36 | <a href="#sec5">Generics with Methods that Implement Protocols</a> |
| 37 | </dt> |
| 38 | </dl> |
| 39 | </dd> |
| 40 | </dl> |
| 41 | </dd> |
| 42 | <dt> |
| 43 | <a href="#sec6">Limitations of Default Language Behavior</a> |
| 44 | </dt> |
| 45 | <dd> |
| 46 | <dl> |
| 47 | <dt> |
| 48 | <a href="#sec7">Slot Storage</a> |
| 49 | </dt> |
| 50 | <dt> |
| 51 | <a href="#sec8">Design Patterns</a> |
| 52 | </dt> |
| 53 | </dl> |
| 54 | </dd> |
| 55 | <dt> |
| 56 | <a href="#sec9">Metasoftware</a> |
| 57 | </dt> |
| 58 | <dd> |
| 59 | <dl> |
| 60 | <dt> |
| 61 | <a href="#sec10">Runtime Generated Classes</a> |
| 62 | </dt> |
| 63 | <dt> |
| 64 | <a href="#sec11">Object Inspection</a> |
| 65 | </dt> |
| 66 | </dl> |
| 67 | </dd> |
| 68 | <dt> |
| 69 | <a href="#sec12">Metaobject Protocols</a> |
| 70 | </dt> |
| 71 | <dd> |
| 72 | <dl> |
| 73 | <dt> |
| 74 | <a href="#sec13">Limited/Generalized Internals of the Implementation</a> |
| 75 | </dt> |
| 76 | <dt> |
| 77 | <a href="#sec14">Classes of MOPs</a> |
| 78 | </dt> |
| 79 | <dd> |
| 80 | <dl> |
| 81 | <dt> |
| 82 | <a href="#sec15">Reflective</a> |
| 83 | </dt> |
| 84 | <dt> |
| 85 | <a href="#sec16">Intercessory</a> |
| 86 | </dt> |
| 87 | </dl> |
| 88 | </dd> |
| 89 | <dt> |
| 90 | <a href="#sec17">Violation of Encapsulation?</a> |
| 91 | </dt> |
| 92 | </dl> |
| 93 | </dd> |
| 94 | <dt> |
| 95 | <a href="#sec18">MOP Design Principles</a> |
| 96 | </dt> |
| 97 | <dd> |
| 98 | <dl> |
| 99 | <dt> |
| 100 | <a href="#sec19">Layered Protocol</a> |
| 101 | </dt> |
| 102 | <dd> |
| 103 | <dl> |
| 104 | <dt> |
| 105 | <a href="#sec20">Top Level <strong>Must</strong> Call Lower Level Methods</a> |
| 106 | </dt> |
| 107 | <dt> |
| 108 | <a href="#sec21">Lower Level Methods are Easier to Customize</a> |
| 109 | </dt> |
| 110 | </dl> |
| 111 | </dd> |
| 112 | <dt> |
| 113 | <a href="#sec22">Functional Where Possible</a> |
| 114 | </dt> |
| 115 | <dd> |
| 116 | <dl> |
| 117 | <dt> |
| 118 | <a href="#sec23">Memoization</a> |
| 119 | </dt> |
| 120 | <dt> |
| 121 | <a href="#sec24">Constant Shared Return Values</a> |
| 122 | </dt> |
| 123 | </dl> |
| 124 | </dd> |
| 125 | <dt> |
| 126 | <a href="#sec25">Procedural Only Where Neccesary</a> |
| 127 | </dt> |
| 128 | <dt> |
| 129 | <a href="#sec26">Real World</a> |
| 130 | </dt> |
| 131 | <dd> |
| 132 | <dl> |
| 133 | <dt> |
| 134 | <a href="#sec27">UCW and Arnesi</a> |
| 135 | </dt> |
| 136 | <dt> |
| 137 | <a href="#sec28">CLSQL</a> |
| 138 | </dt> |
| 139 | <dt> |
| 140 | <a href="#sec29">Elephant</a> |
| 141 | </dt> |
| 142 | </dl> |
| 143 | </dd> |
| 144 | </dl> |
| 145 | </dd> |
| 146 | <dt> |
| 147 | <a href="#sec30">Sources &amp; Further Reading</a> |
| 148 | </dt> |
| 149 | <dd> |
| 150 | <dl> |
| 151 | <dt> |
| 152 | <a href="#sec31">Sources</a> |
| 153 | </dt> |
| 154 | <dd> |
| 155 | <dl> |
| 156 | <dt> |
| 157 | <a href="#sec32">The Art of the Metaobject Protocol</a> |
| 158 | </dt> |
| 159 | <dt> |
| 160 | <a href="#sec33">CLOS MOP Specification</a> |
| 161 | </dt> |
| 162 | <dt> |
| 163 | <a href="#sec34">Metaobject Protocols: Why We Want Them and What Else They Can Do</a> |
| 164 | </dt> |
| 165 | <dt> |
| 166 | <a href="#sec35">Why Are Black Boxes so Hard to Reuse?</a> |
| 167 | </dt> |
| 168 | </dl> |
| 169 | </dd> |
| 170 | <dt> |
| 171 | <a href="#sec36">Further Reading</a> |
| 172 | </dt> |
| 173 | <dd> |
| 174 | <dl> |
| 175 | <dt> |
| 176 | <a href="#sec37">A Metaobject Protocol for C++</a> |
| 177 | </dt> |
| 178 | <dt> |
| 179 | <a href="#sec38">Open Implementations and Metaobject Protocols</a> |
| 180 | </dt> |
| 181 | </dl> |
| 182 | </dd> |
| 183 | </dl> |
| 184 | </dd> |
| 185 | </dl> |
| 186 | </div> |
| 187 | |
| 188 | |
| 189 | <!-- Page published by Emacs Muse begins here --><p>In Fall of 2006 I did a small project on Metaobject Protocols for my |
| 190 | CS 331 class. Here lie my notes which may perhaps be useful to |
| 191 | others. I hope to expand them into something more useful over time.</p> |
| 192 | |
| 193 | <h2><a name="sec1" id="sec1"></a> |
| 194 | Background</h2> |
| 195 | |
| 196 | <h3><a name="sec2" id="sec2"></a> |
| 197 | Object Protocols</h3> |
| 198 | |
| 199 | <p class="first">An object protocol is a set of methods and specification of the |
| 200 | interactions between the methods which provide some generic behavior |
| 201 | (e.g. of a sequence) that are then implemented by classes which |
| 202 | conform to the protocol (e.g. a vector or list). In most object |
| 203 | systems a class contains both the methods which implement a protocol |
| 204 | and the data used by the implementation. The intent is to emulate |
| 205 | state machines which pass messages between each other.</p> |
| 206 | |
| 207 | |
| 208 | <h3><a name="sec3" id="sec3"></a> |
| 209 | CLOS Way of OO</h3> |
| 210 | |
| 211 | <p class="first">The Common Lisp Object System (CLOS) is different. It separates |
| 212 | the data and method concepts into classes and generics. A class |
| 213 | contains data fields only, and a generic has methods specialized for |
| 214 | certain types attached to it. This seems a bit weird at first, but is |
| 215 | significantly more powerful as it encourages complete encapsulation |
| 216 | through its use of classes primarily for method specialization rather |
| 217 | than for state storage.</p> |
| 218 | |
| 219 | <h4><a name="sec4" id="sec4"></a> |
| 220 | Classes for Scratch Data and Types</h4> |
| 221 | |
| 222 | <p class="first">In CLOS classes store data in slots (which are the same as data |
| 223 | members). Encapsulation is not provided; any bit of code can use |
| 224 | <code>slot-value</code> to access or set the value of a slot. This may seem odd at |
| 225 | first, but encapsulation is of questionable importance as the slots |
| 226 | are meant only to be used by the protocol defined around the class.</p> |
| 227 | |
| 228 | <p>Classes are defined with <code>defclass</code></p> |
| 229 | |
| 230 | <pre class="src"> |
| 231 | (<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">name</span> (superclasses ...) |
| 232 | ((slot-name <span style="color: #b0c4de;">:accessor</span> slot-accessor ...) |
| 233 | ...) |
| 234 | (class-options ...)) |
| 235 | |
| 236 | (<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">example</span> () |
| 237 | ((foo <span style="color: #b0c4de;">:accessor</span> foo-of <span style="color: #b0c4de;">:initform</span> 5))) |
| 238 | |
| 239 | (<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">example-child</span> (example) |
| 240 | ((bar <span style="color: #b0c4de;">:accessor</span> bar-of <span style="color: #b0c4de;">:initform</span> (list 1 2 3)))) |
| 241 | </pre> |
| 242 | |
| 243 | <p>Slot defintions have several options; the above example shows only the |
| 244 | <code>:accessor</code> and <code>:initform</code> options which are the most commonly |
| 245 | used. <code>:accessor</code> generates an accessor for the slot (e.g. if you have |
| 246 | an instance of <code>example</code> you can <code>(setf (foo-of some-example-instance) |
| 247 | 'some-value)</code> to set and <code>(foo-of some-example-instance)</code> to access the |
| 248 | value). <code>:initform</code> provides a default initial value for the slot as a |
| 249 | symbolic expression to be evaluated when an instance is created in the |
| 250 | lexical environment of the class definition.</p> |
| 251 | |
| 252 | |
| 253 | <h4><a name="sec5" id="sec5"></a> |
| 254 | Generics with Methods that Implement Protocols</h4> |
| 255 | |
| 256 | <p class="first">Generics are like normal functions in Lisp, but they only provide a |
| 257 | lambda list (parameter list). Methods are added to the generic which |
| 258 | specialize on the types of their parameters and provide an |
| 259 | implementation. This allows writing rich layered protocols which can |
| 260 | enable selective modification of individual facets with minimal code.</p> |
| 261 | |
| 262 | <pre class="src"> |
| 263 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">generic</span> (parameters ...) |
| 264 | (options) ...) |
| 265 | |
| 266 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">generic-name</span> ((parameter type) parameter ...) |
| 267 | <span style="color: #b3b3b3;">"documentation string"</span> |
| 268 | body) |
| 269 | |
| 270 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">foo</span> (bar baz quux) |
| 271 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Process the baz with the quux capacitor to make the |
| 272 | foo widget fly into the sky at warp speed"</span>)) |
| 273 | |
| 274 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">foo</span> ((bar example) baz (quux capacitor)) |
| 275 | (launch bar (process-with quux baz))) |
| 276 | </pre> |
| 277 | |
| 278 | <p>A method lambda list differs from a normal lambda list only in that it |
| 279 | can specify the type of the parameter using the notation <code>(name type)</code>. |
| 280 | Note also that methods can specialize on the types of every |
| 281 | argument and not just the first one. This is quite powerful for |
| 282 | reasons outside of the scope of this presentation.</p> |
| 283 | |
| 284 | |
| 285 | |
| 286 | |
| 287 | <h2><a name="sec6" id="sec6"></a> |
| 288 | Limitations of Default Language Behavior</h2> |
| 289 | |
| 290 | <p class="first">The behavior of a language is a compromise between many competing |
| 291 | issues that attempts to be as generally useful as possible so that |
| 292 | <em>most</em> applications will have no issue with the default behavior. There |
| 293 | are, however, certain applications that could be cleanly written with |
| 294 | minor modifications to the behavior of the language, but would be |
| 295 | impossible or quite difficult to write otherwise.</p> |
| 296 | |
| 297 | <h3><a name="sec7" id="sec7"></a> |
| 298 | Slot Storage</h3> |
| 299 | |
| 300 | <p class="first">Most languages choose to preallocate storage for all of the slots of |
| 301 | an instance. Now imagine a contact database that stores information |
| 302 | about people in slots of a class. There may be dozens of slots, but |
| 303 | often many of them will be left blank. If slot storage is preallocated |
| 304 | much memory will be wasted and the database may not be able to fit |
| 305 | into the memory of the hardware it must run on (perhaps for financial |
| 306 | reasons, huge datasets, etc.).</p> |
| 307 | |
| 308 | <p>To save memory the author of the contact database must implement his |
| 309 | own system to store properties and allocate them lazily. This |
| 310 | represents a fair bit of effort, and would implement a system that |
| 311 | differed from the existing slot system of classes only regarding slot |
| 312 | storage.</p> |
| 313 | |
| 314 | <p>It would be useful if there were a way to customize slot allocation in |
| 315 | instances. The customizations would be minor and require overriding |
| 316 | only the initial allocation behavior and the behavior of the first |
| 317 | assignment to the slot. It is a a trivial problem in a language that |
| 318 | allows customization of these behaviors.</p> |
| 319 | |
| 320 | |
| 321 | <h3><a name="sec8" id="sec8"></a> |
| 322 | Design Patterns</h3> |
| 323 | |
| 324 | <p class="first">Design Patterns are generalized versions of common patterns found in |
| 325 | programs. Many of them are merely methods to get around deficiencies |
| 326 | in the language, and can be quite messy to implement in some |
| 327 | languages. Ideally a pattern would be subsumed by the language, but |
| 328 | real world contraints require language standards to remain fairly |
| 329 | static.</p> |
| 330 | |
| 331 | |
| 332 | |
| 333 | <h2><a name="sec9" id="sec9"></a> |
| 334 | Metasoftware</h2> |
| 335 | |
| 336 | <p class="first">Some types of programs could be written easily if the language were |
| 337 | customizable but are nearly impossible to write when it is not.</p> |
| 338 | |
| 339 | <h3><a name="sec10" id="sec10"></a> |
| 340 | Runtime Generated Classes</h3> |
| 341 | |
| 342 | <p class="first">Say you wanted to write a video game where players could create their |
| 343 | own objects, attach behaviors to the objects, and perhaps mix |
| 344 | different objects together to create new ones. When you abstract the |
| 345 | problem this looks just like an object system! Wouldn't it be nice if |
| 346 | your program could create new classes and methods on the fly portably?</p> |
| 347 | |
| 348 | |
| 349 | <h3><a name="sec11" id="sec11"></a> |
| 350 | Object Inspection</h3> |
| 351 | |
| 352 | <p class="first">Imagine you were developing a complicated program with many different |
| 353 | objects that interacted in fairly complex ways. A tool to inspect the |
| 354 | structure of objects while debugging would be quite useful, but in a |
| 355 | traditional language would be impossible to implement portably. This |
| 356 | could force you to purchase a certain compiler implementation which |
| 357 | provided an inspector, and even then would likely not be customizable.</p> |
| 358 | |
| 359 | <p>This problem can be generalized to apply to most debugging tools; it |
| 360 | would be useful to write such tools portably because users of the |
| 361 | <em>language</em> and not the <em>compiler</em> need to debug software. Sharing |
| 362 | infrastructure would result in better tools (more developers), and |
| 363 | save the man-years of wasted effort that comes with having to rewrite |
| 364 | unportable tools from scratch multiple times.</p> |
| 365 | |
| 366 | |
| 367 | |
| 368 | <h2><a name="sec12" id="sec12"></a> |
| 369 | Metaobject Protocols</h2> |
| 370 | |
| 371 | <h3><a name="sec13" id="sec13"></a> |
| 372 | Limited/Generalized Internals of the Implementation</h3> |
| 373 | |
| 374 | <p class="first">A Metaobject Protocol (MOP) is a generalized and limited subset of the |
| 375 | underlying language implementation. It is limited to allow multiple |
| 376 | implementation strategies; this, along with careful design, is |
| 377 | essential because programming language research is ever advancing and |
| 378 | new techniques for creating more reliable and faster implementations |
| 379 | are still being discovered.</p> |
| 380 | |
| 381 | <p>This subset of the implementation is exported as a set of methods on |
| 382 | metaobjects. Thus the language is implemented in itself. The system |
| 383 | can then be customized using the extension and overriding features of |
| 384 | the language itself.</p> |
| 385 | |
| 386 | |
| 387 | <h3><a name="sec14" id="sec14"></a> |
| 388 | Classes of MOPs</h3> |
| 389 | |
| 390 | <h4><a name="sec15" id="sec15"></a> |
| 391 | Reflective</h4> |
| 392 | |
| 393 | <p class="first">A reflective MOP provides an interface to information <em>about</em> the |
| 394 | running system. It exposes class relationships, the methods attached |
| 395 | to a generic, etc. A reflective MOP often provides some functionality |
| 396 | for creating new classes at runtime. Smalltalk was one of the first |
| 397 | languages to expose a reflective MOP.</p> |
| 398 | |
| 399 | <h5>Example: Object Inspector</h5> |
| 400 | |
| 401 | <pre class="src"> |
| 402 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">example-inspect</span> (instance) |
| 403 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Simple object inspector using CLOS MOP"</span>)) |
| 404 | |
| 405 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">example-inspect</span> ((instance t)) |
| 406 | (format t <span style="color: #b3b3b3;">"Simple Object~% Value: ~S~%"</span> instance)) |
| 407 | |
| 408 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">example-inspect</span> ((instance standard-object)) |
| 409 | (<span style="color: #b9d3ee;">let</span> ((class (class-of instance))) |
| 410 | (format t <span style="color: #b3b3b3;">"Class: ~S, Superclasses: ~S~%"</span> |
| 411 | (class-name class) |
| 412 | (mapcar #'class-name |
| 413 | (class-precedence-list class))) |
| 414 | (<span style="color: #b9d3ee;">let</span> ((slot-names (mapcar #'slot-definition-name |
| 415 | (class-slots class)))) |
| 416 | (format t <span style="color: #b3b3b3;">"Slots: ~%~{ ~S~%~}"</span> slot-names) |
| 417 | (inspect-loop slot-names instance #'example-inspect)))) |
| 418 | |
| 419 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">inspect-loop</span> (slots instance inspector) |
| 420 | (format t <span style="color: #b3b3b3;">"Enter slot to inspect or :pop to go up one level: "</span>) |
| 421 | (finish-output) |
| 422 | (<span style="color: #b9d3ee;">let*</span> ((slot (read)) |
| 423 | (found-slot (member slot slots))) |
| 424 | (<span style="color: #b9d3ee;">cond</span> (found-slot |
| 425 | (funcall inspector (slot-value instance slot)) |
| 426 | (funcall inspector instance)) |
| 427 | ((eq slot <span style="color: #b0c4de;">:pop</span>) t) |
| 428 | (t |
| 429 | (format t <span style="color: #b3b3b3;">"~S is invalid. Valid slot names: ~S~%"</span> |
| 430 | slot |
| 431 | slots) |
| 432 | (inspect-loop slots instance inspector))))) |
| 433 | </pre> |
| 434 | |
| 435 | |
| 436 | <h5>Example: Runtime Generated Classes and Methods</h5> |
| 437 | |
| 438 | |
| 439 | |
| 440 | <h4><a name="sec16" id="sec16"></a> |
| 441 | Intercessory</h4> |
| 442 | |
| 443 | <p class="first">Intercessory MOPs allow the user to customize language behavior by |
| 444 | implementing methods which override certain aspects of the language |
| 445 | behavior. This class of MOPs are what make MOPs especially |
| 446 | powerful. No longer must a problem be restructured to fit the |
| 447 | implementation language; the underyling language can be reshaped to |
| 448 | fit the task at hand, and obfuscation of the intended structure of the |
| 449 | application can be avoided.</p> |
| 450 | |
| 451 | <h5>Example: Lazily Allocated Slots</h5> |
| 452 | |
| 453 | |
| 454 | <h5>Example: Observer Design Pattern</h5> |
| 455 | |
| 456 | <p>A simple implementation of the observer pattern is under 100 lines, |
| 457 | and the user level code requires only a single line of code to make |
| 458 | any existing class observable.</p> |
| 459 | |
| 460 | <p>In a language lacking a MOP, implementing the observer pattern |
| 461 | requires modifying every accessor of a class to explicitly invoke any |
| 462 | observers, and neccesitates the addition of a mixin class to the class |
| 463 | heirarchy. The fact that an object can be observed is a meta property |
| 464 | of the class, and forcing it to be implemented at the application |
| 465 | level dirties the inheritance heirarchy and adds uneccesary meta |
| 466 | details to the program.</p> |
| 467 | |
| 468 | <pre class="src"> |
| 469 | <span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">This metaclass adds a slot to instances which use it, and so the |
| 470 | </span><span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">system is defined in its own package to avoid name conflicts |
| 471 | </span>(<span style="color: #b9d3ee;">defpackage</span> <span style="color: #98fb98;">:observer</span> |
| 472 | (<span style="color: #b0c4de;">:use</span> <span style="color: #b0c4de;">:cl</span> #+sbcl <span style="color: #b0c4de;">:sb-mop</span>) |
| 473 | (<span style="color: #b0c4de;">:export</span> observable register-observer unregister-observer)) |
| 474 | |
| 475 | (<span style="color: #b9d3ee;">in-package</span> <span style="color: #b0c4de;">:observer</span>) |
| 476 | |
| 477 | <span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">Metaclass |
| 478 | </span>(<span style="color: #b9d3ee;">defclass</span> <span style="color: #98fb98;">observable</span> (standard-class) |
| 479 | () |
| 480 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Metaclass for observable objects"</span>)) |
| 481 | |
| 482 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">compute-slots</span> ((class observable)) |
| 483 | <span style="color: #b3b3b3;">"Add a slot for storing observers to observable instances"</span> |
| 484 | (cons (make-instance 'standard-effective-slot-definition |
| 485 | <span style="color: #b0c4de;">:name</span> 'observers |
| 486 | <span style="color: #b0c4de;">:initform</span> '(make-hash-table) |
| 487 | <span style="color: #b0c4de;">:initfunction</span> #'(<span style="color: #b9d3ee;">lambda</span> () (make-hash-table))) |
| 488 | (call-next-method))) |
| 489 | |
| 490 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">validate-superclass</span> ((class observable) |
| 491 | (super standard-class)) |
| 492 | t) |
| 493 | |
| 494 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">register-observer</span> (instance slot-name key closure) |
| 495 | (register-observer-with-class (class-of instance) |
| 496 | instance |
| 497 | slot-name |
| 498 | key |
| 499 | closure)) |
| 500 | |
| 501 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">unregister-observer</span> (instance slot-name key) |
| 502 | (unregister-observer-with-class (class-of instance) |
| 503 | instance |
| 504 | slot-name |
| 505 | key)) |
| 506 | |
| 507 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">get-observers</span> (instance slot-name) |
| 508 | (get-observers-with-class (class-of instance) |
| 509 | instance |
| 510 | slot-name)) |
| 511 | |
| 512 | (<span style="color: #b9d3ee;">defun</span> <span style="color: #87cefa;">add-observer-table</span> (instance slot-name) |
| 513 | (setf (gethash slot-name (slot-value instance |
| 514 | 'observers)) |
| 515 | (make-hash-table))) |
| 516 | |
| 517 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">register-observer-with-class</span> (class instance slot-name key closure)) |
| 518 | (<span style="color: #b9d3ee;">defgeneric</span> <span style="color: #87cefa;">unregister-observer-with-class</span> (class |
| 519 | instance |
| 520 | slot-name |
| 521 | key)) |
| 522 | |
| 523 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">register-observer-with-class</span> ((class observable) |
| 524 | instance |
| 525 | slot-name |
| 526 | key |
| 527 | closure) |
| 528 | (setf (gethash key |
| 529 | (or (gethash slot-name |
| 530 | (slot-value instance 'observers)) |
| 531 | <span style="color: #ff7f24;">;; </span><span style="color: #ff7f24;">Lazily add observer hash tables |
| 532 | </span> (add-observer-table instance slot-name))) |
| 533 | closure)) |
| 534 | |
| 535 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">unregister-observer-with-class</span> ((class observable) |
| 536 | instance |
| 537 | slot-name |
| 538 | key) |
| 539 | (remhash key (gethash slot-name |
| 540 | (slot-value instance 'observers)))) |
| 541 | |
| 542 | (<span style="color: #b9d3ee;">defmethod</span> <span style="color: #87cefa;">get-observers-with-class</span> ((class observable) |
| 543 | instance |
| 544 | slot-name) |
| 545 | (gethash slot-name (slot-value instance 'observers))) |
| 546 | |
| 547 | (<span style="color: #b9d3ee;">defmethod</span> (<span style="color: #87cefa;">setf slot-value-using-class)</span> <span style="color: #b0c4de;">:before</span> (new-value |
| 548 | (class observable) |
| 549 | instance |
| 550 | slot) |
| 551 | (<span style="color: #b9d3ee;">let</span> ((slot-name (slot-definition-name slot))) |
| 552 | (<span style="color: #b9d3ee;">if</span> (not (eq 'observers slot-name)) |
| 553 | (<span style="color: #b9d3ee;">let</span> ((observers |
| 554 | (get-observers instance (slot-definition-name slot)))) |
| 555 | (<span style="color: #b9d3ee;">if</span> observers |
| 556 | (maphash #'(<span style="color: #b9d3ee;">lambda</span> (key observer) |
| 557 | (funcall observer |
| 558 | (<span style="color: #b9d3ee;">if</span> (slot-boundp instance slot-name) |
| 559 | (slot-value instance slot-name) |
| 560 | nil) |
| 561 | new-value)) |
| 562 | observers)))))) |
| 563 | </pre> |
| 564 | |
| 565 | |
| 566 | |
| 567 | |
| 568 | |
| 569 | <h3><a name="sec17" id="sec17"></a> |
| 570 | Violation of Encapsulation?</h3> |
| 571 | |
| 572 | <p class="first">A MOP may seem like a violation of encapsulation by revealing some |
| 573 | implementation details, but in reality a well designed protocol does |
| 574 | not reveal anything which was not already exposed. Implementation |
| 575 | decisions affect users, and some of these details do leak through to |
| 576 | higher levels (e.g. the memory layout of slots). Implicit in the |
| 577 | protocol specification are these implementation details, and the MOP |
| 578 | merely makes this limited subset available for customization.</p> |
| 579 | |
| 580 | <p>A MOP makes it possible to customize certain implementation decisions |
| 581 | that do not <strong>radically</strong> alter the behavior of the base language. The |
| 582 | conceptual vocabulary of the system retains its meaning, and so code |
| 583 | written in one dialect can interact with code written in another |
| 584 | without knowing that they speak different ones.</p> |
| 585 | |
| 586 | |
| 587 | |
| 588 | <h2><a name="sec18" id="sec18"></a> |
| 589 | MOP Design Principles</h2> |
| 590 | |
| 591 | <h3><a name="sec19" id="sec19"></a> |
| 592 | Layered Protocol</h3> |
| 593 | |
| 594 | <p class="first">A layered protocol design is good for both meta and normal object |
| 595 | protocols, and enables a combinatorial explosion of customizations to |
| 596 | the protocol.</p> |
| 597 | |
| 598 | <h4><a name="sec20" id="sec20"></a> |
| 599 | Top Level <strong>Must</strong> Call Lower Level Methods</h4> |
| 600 | |
| 601 | <p class="first">The top level methods of a layered protocol are required to call |
| 602 | certain lower level methods to perform some tasks. This both makes it |
| 603 | easier to customize the top level methods (which perform very broad |
| 604 | tasks) by providing some pieces of implementation for the programmer, |
| 605 | and enables more customization by opening up the replacement of lower |
| 606 | level functions as a way to alter a small detail of the high level |
| 607 | behavior.</p> |
| 608 | |
| 609 | |
| 610 | <h4><a name="sec21" id="sec21"></a> |
| 611 | Lower Level Methods are Easier to Customize</h4> |
| 612 | |
| 613 | <p class="first">The lower level methods of a MOP are limited in scope and can be |
| 614 | implemented easily. Often the desired changes to language behavior are |
| 615 | minor, and having methods that perform simple tasks which are often |
| 616 | customized reduces the effort required to extend the system.</p> |
| 617 | |
| 618 | |
| 619 | |
| 620 | <h3><a name="sec22" id="sec22"></a> |
| 621 | Functional Where Possible</h3> |
| 622 | |
| 623 | <p class="first">Functional protocols are preferred for MOPs (and object protocols in |
| 624 | general). Functional protocols open up several optimizations for the |
| 625 | implementation without burdening the user of the protocol.</p> |
| 626 | |
| 627 | <h4><a name="sec23" id="sec23"></a> |
| 628 | Memoization</h4> |
| 629 | |
| 630 | <p class="first">Memoization is the process of saving the results of a function call |
| 631 | for future use. This avoids expensive recomputation of values which |
| 632 | have not changed (recall that a true function will always return the |
| 633 | same result when given the same arguments).</p> |
| 634 | |
| 635 | <p>A functional MOP can be optimized easily by exploiting this property |
| 636 | to memoize the return values of calls to expensive operations. A MOP |
| 637 | must be be very fast to avoid making programs unusably slow, and |
| 638 | memoization is able to give an appreciable speedup in many cases |
| 639 | without a significant burden on memory usage.</p> |
| 640 | |
| 641 | |
| 642 | <h4><a name="sec24" id="sec24"></a> |
| 643 | Constant Shared Return Values</h4> |
| 644 | |
| 645 | <p class="first">Disallowing modification of values returned by protocol methods allows |
| 646 | the implementation to return large data structures by reference to |
| 647 | avoid expensive copying without having to do expensive data integrity |
| 648 | checks or copying.</p> |
| 649 | |
| 650 | |
| 651 | |
| 652 | <h3><a name="sec25" id="sec25"></a> |
| 653 | Procedural Only Where Neccesary</h3> |
| 654 | |
| 655 | <p class="first">Some operations like method invocation are inheretly stateful and so |
| 656 | must use a procedural protocol. There is no benefit to be gained from |
| 657 | using a functional protocol, and indeed an attempt would result in |
| 658 | obtuse code that severely restricted the implementian. Do note that |
| 659 | only a very small part of method invocation is stateful (the actual |
| 660 | call), and most of it can be implemented functionally (e.g. computing |
| 661 | the discriminating function).</p> |
| 662 | |
| 663 | |
| 664 | <h3><a name="sec26" id="sec26"></a> |
| 665 | Real World</h3> |
| 666 | |
| 667 | <h4><a name="sec27" id="sec27"></a> |
| 668 | <a href="http://common-lisp.net/project/ucw/">UCW</a> and <a href="http://common-lisp.net/project/bese/arnesi.html">Arnesi</a></h4> |
| 669 | |
| 670 | <p class="first">Arnesi uses the CLOS MOP to implement methods which are transparantly |
| 671 | rewritten into continuation passing style. This allows their execution |
| 672 | to be suspended at certain points and resumed later. UCW builds on top |
| 673 | of this to support a web framework where the statelessness of http is |
| 674 | hidden from the user; displaying a page suspends the execution of the |
| 675 | current continuation, and resumes it upon submission. The user level |
| 676 | code is completely unaware of this.</p> |
| 677 | |
| 678 | |
| 679 | <h4><a name="sec28" id="sec28"></a> |
| 680 | <a href="http://clsql.b9.com">CLSQL</a></h4> |
| 681 | |
| 682 | <p class="first">CLSQL uses the reflective part of the CLOS MOP to map Common Lisp data |
| 683 | types into SQL types, and the intercessory protocol for slot |
| 684 | allocation to map slots onto database columns or sql expressions (for |
| 685 | implementing relational slots).</p> |
| 686 | |
| 687 | |
| 688 | <h4><a name="sec29" id="sec29"></a> |
| 689 | <a href="http://common-lisp.net/project/elephant/">Elephant</a></h4> |
| 690 | |
| 691 | <p class="first">Elephant uses the CLOS MOP to transparantly store any class to disk |
| 692 | and handle paging between the disk store and memory efficiently |
| 693 | without user intervention.</p> |
| 694 | |
| 695 | |
| 696 | |
| 697 | |
| 698 | <h2><a name="sec30" id="sec30"></a> |
| 699 | Sources &amp; Further Reading</h2> |
| 700 | |
| 701 | <h3><a name="sec31" id="sec31"></a> |
| 702 | Sources</h3> |
| 703 | |
| 704 | <h4><a name="sec32" id="sec32"></a> |
| 705 | The Art of the Metaobject Protocol</h4> |
| 706 | |
| 707 | <h5>Kiczales, Gregor et al. MIT Press 1991</h5> |
| 708 | |
| 709 | <p>Highly recommended reading even if you plan to never implement a MOP |
| 710 | or use the CLOS one. The design principles it recommends are quite |
| 711 | useful.</p> |
| 712 | |
| 713 | |
| 714 | |
| 715 | <h4><a name="sec33" id="sec33"></a> |
| 716 | <a href="http://www.lisp.org/mop/contents.html">CLOS MOP Specification</a></h4> |
| 717 | |
| 718 | <p class="first">Specification of the MOP for CLOS defined in <em>The Art of the Metaobject Protocol</em>.</p> |
| 719 | |
| 720 | |
| 721 | <h4><a name="sec34" id="sec34"></a> |
| 722 | <a href="http://citeseer.ist.psu.edu/399658.html">Metaobject Protocols: Why We Want Them and What Else They Can Do</a></h4> |
| 723 | |
| 724 | <p class="first">A short overview of MOP design principles followed by three example |
| 725 | metaobject protocols for Scheme.</p> |
| 726 | |
| 727 | |
| 728 | <h4><a name="sec35" id="sec35"></a> |
| 729 | <a href="http://www2.parc.com/csl/groups/sda/projects/oi/towards-talk/transcript.html">Why Are Black Boxes so Hard to Reuse?</a></h4> |
| 730 | |
| 731 | <p class="first">Transcription of a talk on the benefits of open implementations of |
| 732 | software. It first discusses several problems that black box software |
| 733 | implementations pose, and then presents existing solutions. It shows |
| 734 | how the existing solutions are insufficient, and then provides |
| 735 | metaobject protocols as a solution to most of the problems.</p> |
| 736 | |
| 737 | |
| 738 | |
| 739 | <h3><a name="sec36" id="sec36"></a> |
| 740 | Further Reading</h3> |
| 741 | |
| 742 | <h4><a name="sec37" id="sec37"></a> |
| 743 | <a href="http://citeseer.ist.psu.edu/chiba95metaobject.html">A Metaobject Protocol for C++</a></h4> |
| 744 | |
| 745 | <p class="first">Example of a purely compile time MOP. It implements the functionality |
| 746 | of a code walker and something similar to the Lisp macro system.</p> |
| 747 | |
| 748 | |
| 749 | <h4><a name="sec38" id="sec38"></a> |
| 750 | <a href="http://www.parc.com/csl/groups/sda/publications/papers/Kiczales-TUT95/for-web.pdf">Open Implementations and Metaobject Protocols</a></h4> |
| 751 | |
| 752 | <p class="first">It is a bit long, but it seems to follow a similar structure to AMOP |
| 753 | in introducing MOPs and their usefulness. The pages are slides with |
| 754 | notes, and so the 331 pages might not actually take that long to read.</p> |
| 755 | |
| 756 | |
| 757 | |
| 758 | |
| 759 | <!-- Page published by Emacs Muse ends here --> |
| 760 | |
| 761 | <p class="cke-buttons"> |
| 762 | <!-- validating badges, any browser, etc --> |
| 763 | <a href="http://validator.w3.org/check/referer"><img |
| 764 | src="http://www.w3.org/Icons/valid-xhtml10" |
| 765 | alt="Valid XHTML 1.0!" /></a> |
| 766 | |
| 767 | <a href="http://www.anybrowser.org/campaign/"><img |
| 768 | src="img/buttons/w3c_ab.png" alt="[ Viewable With Any Browser |
| 769 | ]" /></a> |
| 770 | |
| 771 | <a href="http://www.debian.org/"><img |
| 772 | src="img/buttons/debian.png" alt="[ Powered by Debian ]" /></a> |
| 773 | |
| 774 | <a href="http://hcoop.net/"> |
| 775 | <img src="img/buttons/hcoop.png" |
| 776 | alt="[ Hosted by HCoop]" /> |
| 777 | </a> |
| 778 | |
| 779 | <a href="http://www.fsf.org/register_form?referrer=114"> |
| 780 | <img src="img/buttons/fsf_member.png" |
| 781 | alt="[ FSF Associate Member ]" /> |
| 782 | </a> |
| 783 | </p> |
| 784 | |
| 785 | <p class="cke-footer">* jeffcovey becomes too groggy to read the directions and becomes |
| 786 | the year's first nasal spray overdose fatality. |
| 787 | </p> |
| 788 | <p class="cke-timestamp">Last Modified: |
| 789 | September 26, 2008</p> |
| 790 | </body> |
| 791 | </html> |